#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/cdev.h>

#include <linux/kernel.h>

#include "led_info.h"


//设计一个类型，描述一个设备的信息
struct led_desc{
	unsigned int dev_major;//设备号
	struct class *cls;
	struct device *dev;//创建设备文件
	char *name;
};

struct my_dev_desc{
		char *name;
		unsigned int *gp__con;
		unsigned int *gp__dat;
		int offset;
};

struct led_desc *plat_led;
struct my_dev_desc my_drv_desc[LED_Quantity];


void my_led_on(int nr)
{
	if((nr > 0)&&(nr <= LED_Quantity))
	{
		writel(readl(my_drv_desc[nr-1].gp__dat) | 1 << my_drv_desc[nr-1].offset, my_drv_desc[nr-1].gp__dat);
	}
	else
	{
		printk("input error!\n");
	}
}

void my_led_off(int nr)
{
	if((nr > 0)&&(nr <= LED_Quantity))
		{
			writel(readl(my_drv_desc[nr-1].gp__dat) & ~(1 << my_drv_desc[nr-1].offset), my_drv_desc[nr-1].gp__dat);
		}
		else
		{
			printk("input error!\n");
		}

}

static int my_led_open(struct inode *inode, struct file *file)
{
	return 0;
}
	
static int my_led_release(struct inode *inode, struct file *file)
{
	return 0;
}
	
static long my_led_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int nr;

	if(copy_from_user((void *)&nr, (void *)arg, sizeof(nr)))
		return -EFAULT;

	if (nr < 1 || nr > 4)
		return -EINVAL;

	switch (cmd) {
		case LED_ON:
			my_led_on(nr);
			break;
		case LED_OFF:
			my_led_off(nr);
			break;
		default:
			printk("Invalid argument");
			return -EINVAL;
	}

	return 0;
}

struct file_operations my_fops = {
	.owner = THIS_MODULE,
	.open = my_led_open,
	.release = my_led_release,
	.unlocked_ioctl = my_led_unlocked_ioctl,
};

int my_led_ioremap(struct resource *res)//接收device 信息 并且 对地址映射
{
	int ret,i;
	for(i = 0;i < LED_Quantity;i++){
		my_drv_desc[i].offset = simple_strtol(res->name,&my_drv_desc[i].name,0);//提取 偏移量      和 名字
		
		my_drv_desc[i].gp__con = ioremap(res->start, 4);//地址映射
		if (my_drv_desc[i].gp__con == NULL) {
			ret = -ENOMEM;
			return ret;
		}	
		my_drv_desc[i].gp__dat = ioremap(res->end, 1);
		if (my_drv_desc[i].gp__dat == NULL) {
			ret = -ENOMEM;
			return ret;
		}		
		res++;
	}
	return 0;
}

void my_led_io_init(void)
{
	int i;
	for(i=0;i<LED_Quantity;i++)
	{
		writel((readl(my_drv_desc[i].gp__con) & ~(0xf<< my_drv_desc[i].offset*4)) | (0x1<<my_drv_desc[i].offset*4), my_drv_desc[i].gp__con);
		writel(readl(my_drv_desc[i].gp__dat) & ~(0x1<<my_drv_desc[i].offset), my_drv_desc[i].gp__dat);
	}
}

void my_led_iounmap(void)		//注销映射地址
{
	int i;
	for(i=0;i<LED_Quantity;i++)
	{
		iounmap(my_drv_desc[i].gp__con);
		iounmap(my_drv_desc[i].gp__dat);
	}
}

int led_pdrv_probe(struct platform_device *pdev)
{
	int ret;
	struct resource *res; //获取到的内存资源
	printk("---------------%s---------------\n",__FUNCTION__);
	//0.实例化全局的设备对象 --分配空间
	plat_led = kmalloc(sizeof(struct led_desc),GFP_KERNEL);
	if(plat_led==NULL)
	{
		printk(KERN_ERR "kmalloc error \n");
		return -ENOMEM;
	}
	//1.一般申请设备资源
	//申请设备号
	plat_led->name="plat_led_text";
	
	plat_led->dev_major = register_chrdev(0, plat_led->name, &my_fops);
	if(plat_led->dev_major < 0){
		printk(KERN_ERR"register_chrdev error !\n");
		ret = -ENODEV;
		goto err_0;
	}
	//2.自动创建一个设备文件
	plat_led->cls = class_create(THIS_MODULE,plat_led->name);//创建一个LED类
	if(IS_ERR(plat_led->cls))
	{
		printk(KERN_ERR"class_create error !\n");
		ret = PTR_ERR(plat_led->cls);//将指针出错的具体原因转化成一个出错码
		goto err_1;
	}
	plat_led->dev = device_create(plat_led->cls,NULL,MKDEV(plat_led->dev_major,0),NULL,"led%d",1);//创建LED设备文件节点
	if(IS_ERR(plat_led->dev))
	{
		printk(KERN_ERR"device_create error !\n");
		ret = PTR_ERR(plat_led->dev);//将指针出错的具体原因转化成一个出错码
		goto err_2;
	}
	
	res = pdev->resource;
	ret = my_led_ioremap(res);
	if (ret < 0)
		goto err_3;
	printk("LED init OK!\n");
	return 0;
err_3:
	device_destroy(plat_led->cls,MKDEV(plat_led->dev_major,0));
err_2:
	class_destroy(plat_led->cls);
err_1:
	unregister_chrdev(plat_led->dev_major, plat_led->name);
err_0:
	kfree(plat_led);
	return ret;
	
	return 0;
}
int led_pdrv_remove(struct platform_device *pdev)
{

	printk("---------------%s---------------\n",__FUNCTION__);
	//一般释放设备资源
	my_led_iounmap();
	//销毁创建了的设备节点
	device_destroy(plat_led->cls,MKDEV(plat_led->dev_major,0));
	class_destroy(plat_led->cls);	
	//取消申请了的设备号
	unregister_chrdev(plat_led->dev_major,plat_led->name);
	
	printk("LED exit\n");
	
	return 0;
}

const struct platform_device_id led_id_table[]={
		{"my_platform_led",0x666},
		{"your_platform_led",0x555},
};

struct platform_driver led_pdrv={
	.probe = led_pdrv_probe,
	.remove = led_pdrv_remove,
	.driver ={
		.name = "my_platform_led",//可用于做匹配 // /sys/bus/platform/drivers/...
	},
	.id_table = led_id_table,

};

static int __init plat_led_drv_init(void)
{
	printk("---------------%s---------------\n",__FUNCTION__);
	//注册一个platform_driver
	return platform_driver_register(&led_pdrv);
	
}

static void __exit plat_led_drv_exit(void)
{
	printk("---------------%s---------------\n",__FUNCTION__);
	//注销
	platform_driver_unregister(&led_pdrv);
}

module_init(plat_led_drv_init);
module_exit(plat_led_drv_exit);
MODULE_LICENSE("GPL");
